/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

/*
 * Licensed to Elasticsearch B.V. under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch B.V. licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.client.opensearch.indices;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.opensearch.client.opensearch._types.ErrorResponse;
import org.opensearch.client.opensearch._types.ExpandWildcard;
import org.opensearch.client.opensearch._types.RequestBase;
import org.opensearch.client.transport.Endpoint;
import org.opensearch.client.transport.endpoints.SimpleEndpoint;
import org.opensearch.client.util.ApiTypeHelper;
import org.opensearch.client.util.ObjectBuilder;
import org.opensearch.client.util.ObjectBuilderBase;

// typedef: indices.get_field_mapping.Request

/**
 * Returns mapping for one or more fields.
 *
 */

public class GetFieldMappingRequest extends RequestBase {
    @Nullable
    private final Boolean allowNoIndices;

    private final List<ExpandWildcard> expandWildcards;

    private final List<String> fields;

    @Nullable
    private final Boolean ignoreUnavailable;

    @Nullable
    private final Boolean includeDefaults;

    private final List<String> index;

    @Nullable
    private final Boolean local;

    // ---------------------------------------------------------------------------------------------

    private GetFieldMappingRequest(Builder builder) {

        this.allowNoIndices = builder.allowNoIndices;
        this.expandWildcards = ApiTypeHelper.unmodifiable(builder.expandWildcards);
        this.fields = ApiTypeHelper.unmodifiableRequired(builder.fields, this, "fields");
        this.ignoreUnavailable = builder.ignoreUnavailable;
        this.includeDefaults = builder.includeDefaults;
        this.index = ApiTypeHelper.unmodifiable(builder.index);
        this.local = builder.local;

    }

    public static GetFieldMappingRequest of(Function<Builder, ObjectBuilder<GetFieldMappingRequest>> fn) {
        return fn.apply(new Builder()).build();
    }

    /**
     * Whether to ignore if a wildcard indices expression resolves into no concrete
     * indices. (This includes <code>_all</code> string or when no indices have been
     * specified)
     * <p>
     * API name: {@code allow_no_indices}
     */
    @Nullable
    public final Boolean allowNoIndices() {
        return this.allowNoIndices;
    }

    /**
     * Whether to expand wildcard expression to concrete indices that are open,
     * closed or both.
     * <p>
     * API name: {@code expand_wildcards}
     */
    public final List<ExpandWildcard> expandWildcards() {
        return this.expandWildcards;
    }

    /**
     * Required - A comma-separated list of fields
     * <p>
     * API name: {@code fields}
     */
    public final List<String> fields() {
        return this.fields;
    }

    /**
     * Whether specified concrete indices should be ignored when unavailable
     * (missing or closed)
     * <p>
     * API name: {@code ignore_unavailable}
     */
    @Nullable
    public final Boolean ignoreUnavailable() {
        return this.ignoreUnavailable;
    }

    /**
     * Whether the default mapping values should be returned as well
     * <p>
     * API name: {@code include_defaults}
     */
    @Nullable
    public final Boolean includeDefaults() {
        return this.includeDefaults;
    }

    /**
     * A comma-separated list of index names
     * <p>
     * API name: {@code index}
     */
    public final List<String> index() {
        return this.index;
    }

    /**
     * Return local information, do not retrieve the state from cluster-manager node
     * (default: false)
     * <p>
     * API name: {@code local}
     */
    @Nullable
    public final Boolean local() {
        return this.local;
    }

    // ---------------------------------------------------------------------------------------------

    /**
     * Builder for {@link GetFieldMappingRequest}.
     */

    public static class Builder extends ObjectBuilderBase implements ObjectBuilder<GetFieldMappingRequest> {
        @Nullable
        private Boolean allowNoIndices;

        @Nullable
        private List<ExpandWildcard> expandWildcards;

        private List<String> fields;

        @Nullable
        private Boolean ignoreUnavailable;

        @Nullable
        private Boolean includeDefaults;

        @Nullable
        private List<String> index;

        @Nullable
        private Boolean local;

        /**
         * Whether to ignore if a wildcard indices expression resolves into no concrete
         * indices. (This includes <code>_all</code> string or when no indices have been
         * specified)
         * <p>
         * API name: {@code allow_no_indices}
         */
        public final Builder allowNoIndices(@Nullable Boolean value) {
            this.allowNoIndices = value;
            return this;
        }

        /**
         * Whether to expand wildcard expression to concrete indices that are open,
         * closed or both.
         * <p>
         * API name: {@code expand_wildcards}
         * <p>
         * Adds all elements of <code>list</code> to <code>expandWildcards</code>.
         */
        public final Builder expandWildcards(List<ExpandWildcard> list) {
            this.expandWildcards = _listAddAll(this.expandWildcards, list);
            return this;
        }

        /**
         * Whether to expand wildcard expression to concrete indices that are open,
         * closed or both.
         * <p>
         * API name: {@code expand_wildcards}
         * <p>
         * Adds one or more values to <code>expandWildcards</code>.
         */
        public final Builder expandWildcards(ExpandWildcard value, ExpandWildcard... values) {
            this.expandWildcards = _listAdd(this.expandWildcards, value, values);
            return this;
        }

        /**
         * Required - A comma-separated list of fields
         * <p>
         * API name: {@code fields}
         * <p>
         * Adds all elements of <code>list</code> to <code>fields</code>.
         */
        public final Builder fields(List<String> list) {
            this.fields = _listAddAll(this.fields, list);
            return this;
        }

        /**
         * Required - A comma-separated list of fields
         * <p>
         * API name: {@code fields}
         * <p>
         * Adds one or more values to <code>fields</code>.
         */
        public final Builder fields(String value, String... values) {
            this.fields = _listAdd(this.fields, value, values);
            return this;
        }

        /**
         * Whether specified concrete indices should be ignored when unavailable
         * (missing or closed)
         * <p>
         * API name: {@code ignore_unavailable}
         */
        public final Builder ignoreUnavailable(@Nullable Boolean value) {
            this.ignoreUnavailable = value;
            return this;
        }

        /**
         * Whether the default mapping values should be returned as well
         * <p>
         * API name: {@code include_defaults}
         */
        public final Builder includeDefaults(@Nullable Boolean value) {
            this.includeDefaults = value;
            return this;
        }

        /**
         * A comma-separated list of index names
         * <p>
         * API name: {@code index}
         * <p>
         * Adds all elements of <code>list</code> to <code>index</code>.
         */
        public final Builder index(List<String> list) {
            this.index = _listAddAll(this.index, list);
            return this;
        }

        /**
         * A comma-separated list of index names
         * <p>
         * API name: {@code index}
         * <p>
         * Adds one or more values to <code>index</code>.
         */
        public final Builder index(String value, String... values) {
            this.index = _listAdd(this.index, value, values);
            return this;
        }

        /**
         * Return local information, do not retrieve the state from cluster-manager node
         * (default: false)
         * <p>
         * API name: {@code local}
         */
        public final Builder local(@Nullable Boolean value) {
            this.local = value;
            return this;
        }

        /**
         * Builds a {@link GetFieldMappingRequest}.
         *
         * @throws NullPointerException
         *             if some of the required fields are null.
         */
        public GetFieldMappingRequest build() {
            _checkSingleUse();

            return new GetFieldMappingRequest(this);
        }
    }

    // ---------------------------------------------------------------------------------------------

    /**
     * Endpoint "{@code indices.get_field_mapping}".
     */
    public static final Endpoint<GetFieldMappingRequest, GetFieldMappingResponse, ErrorResponse> _ENDPOINT = new SimpleEndpoint<>(

        // Request method
        request -> {
            return "GET";

        },

        // Request path
        request -> {
            final int _index = 1 << 0;
            final int _fields = 1 << 1;

            int propsSet = 0;

            if (ApiTypeHelper.isDefined(request.index())) propsSet |= _index;
            propsSet |= _fields;

            if (propsSet == (_fields)) {
                StringBuilder buf = new StringBuilder();
                buf.append("/_mapping");
                buf.append("/field");
                buf.append("/");
                SimpleEndpoint.pathEncode(request.fields.stream().map(v -> v).collect(Collectors.joining(",")), buf);
                return buf.toString();
            }
            if (propsSet == (_index | _fields)) {
                StringBuilder buf = new StringBuilder();
                buf.append("/");
                SimpleEndpoint.pathEncode(request.index.stream().map(v -> v).collect(Collectors.joining(",")), buf);
                buf.append("/_mapping");
                buf.append("/field");
                buf.append("/");
                SimpleEndpoint.pathEncode(request.fields.stream().map(v -> v).collect(Collectors.joining(",")), buf);
                return buf.toString();
            }
            throw SimpleEndpoint.noPathTemplateFound("path");

        },

        // Request parameters
        request -> {
            Map<String, String> params = new HashMap<>();
            if (ApiTypeHelper.isDefined(request.expandWildcards)) {
                params.put("expand_wildcards", request.expandWildcards.stream().map(v -> v.jsonValue()).collect(Collectors.joining(",")));
            }
            if (request.ignoreUnavailable != null) {
                params.put("ignore_unavailable", String.valueOf(request.ignoreUnavailable));
            }
            if (request.allowNoIndices != null) {
                params.put("allow_no_indices", String.valueOf(request.allowNoIndices));
            }
            if (request.includeDefaults != null) {
                params.put("include_defaults", String.valueOf(request.includeDefaults));
            }
            if (request.local != null) {
                params.put("local", String.valueOf(request.local));
            }
            return params;

        },
        SimpleEndpoint.emptyMap(),
        false,
        GetFieldMappingResponse._DESERIALIZER
    );
}
